package ms.core.gateway.utils;

import java.io.File;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;

import ms.core.common.*;
import ms.core.common.consts.SysErr;
import ms.core.common.utils.RequestUtils;
import ms.core.common.utils.UploadedFileUtils.FileDoc;
import ms.core.db.mysql.DbUtilsDrv;
import ms.core.gateway.bean.*;
import ms.core.gateway.dao.*;
import ms.core.gateway.dto.*;
import ms.core.gateway.request.*;
import ms.core.gateway.response.SysRoleVO;
import ms.core.gateway.response.SysUserVO;
import ms.core.gateway.system.Application;
import ms.core.gateway.tool.SessionTool;
import ms.core.tool.*;
import ms.core.tool.MailTool.MailMsg;

public class SysUserUtils {
	private SysUserDao dao;

	public SysUserUtils(){ dao = new SysUserDao(); }

	/**
	 * 用户注册
	 * @param usr
	 * @return
	 */
	public SysUserVO regUser(RegUserRequest usr) {
		if (dao.getUserByAccount(usr.getAccount())!=null)
			throw new AppException(SysErr.SYS_DATA_EXISTS, "此帐号已注册.");

		try {
			DbUtilsDrv.startTransaction();
			// 1. 写入企业信息
			String corpUid = SysCorpUtils.registerCorp(usr.getCtype(), usr.getCorpName(), usr.getAccount(), usr.getMobile());
			// 2. 写入帐号信息
			String uid = dao.registerUser(corpUid, usr);
			// 3. 生成管理员角色
			SysRoleUtils.createAdminRole(corpUid, usr.getCtype(), false);

			DbUtilsDrv.commitTransaction();
			return dao.getUserByUid(uid);
		} catch(AppException e) {
			DbUtilsDrv.rollTransaction();
			throw new AppException(e);
		}
	}

	/**
	 * 添加用户
	 * 
	 * @param usr
	 * @return
	 */
	public void newUser(CreateUserRequest usr) {
		if (dao.getUserByAccount(usr.getAccount())!=null)
			throw new AppException(SysErr.SYS_DATA_EXISTS, "此邮箱或手机号已注册.");

		SysCorpBean corp = SysCorpUtils.getCorp(usr.getCorpUid());
		if (corp==null)
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "新增帐号对应企业信息未建档.");

		if (!StrTool.isBlank(usr.getDeptUid()) && SysDeptUtils.getDept(usr.getDeptUid())==null)
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "新增帐号对应部门信息未建档.");

		// 开通企业主帐号时,创建主角色
		if (usr.getState()!=SysUserBean.STATE_FROZEN && usr.getRoot()==SysUserBean.ROOT_ACCOUNT) {
			SysRoleUtils.createAdminRole(usr.getCorpUid(), corp.getCtype(), false);
		}

		String uid = dao.newUser(corp.getCtype(), usr);
		SysUserVO ret = dao.getUserByUid(uid);
		if (Application.userDelegate!=null) Application.userDelegate.afterNewUser(ret);
	}

	/**
	 * 修改个人信息
	 * 
	 * @param usr
	 * @return
	 */
	public void editSelf(String uid, UpdateSelfRequest usr) {
		SysUserVO exist = dao.getUserByUid(uid);
		if (exist==null)
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND,"用户不存在");

		if (!StrTool.isBlank(usr.getDeptUid()) && SysDeptUtils.getDept(usr.getDeptUid())==null)
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND,"无此部门.");

		dao.editSelf(exist.getId(), usr);
		SysUserVO ret = dao.getUser(exist.getId());
		if (Application.userDelegate!=null) Application.userDelegate.afterUpdateUser(ret);
	}

	/**
	 * 修改用户信息
	 * 
	 * @param corpUid 企业Uid，防止企业间数据互相篡改
	 * @param usr
	 * @return
	 */
	public void editUser(String corpUid, UpdateUserRequest usr) {
		SysUserVO exist = dao.getUserByUid(usr.getUid());
		if (exist==null || (!StrTool.isBlank(corpUid) && !corpUid.equals(exist.getCorpUid())))
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND,"用户不存在");
		
		if (!StrTool.isBlank(usr.getDeptUid()) && SysDeptUtils.getDept(usr.getDeptUid())==null)
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND,"无此部门.");

		dao.editUser(exist.getId(), usr);
		SysUserVO ret = dao.getUser(exist.getId());
		if (Application.userDelegate!=null) Application.userDelegate.afterUpdateUser(ret);
	}

	/**
	 * 修改用户肖像
	 * 
	 * @param uid
	 * @param files 图片文件,记录只有1条
	 * @return
	 */
	public int updatePortrait(String uid, List<FileDoc> files) {
		SysUserBean exist = dao.getUserByUid(uid);
		if (exist==null) {
			RequestUtils.deleteAnnexs(files);
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "用户不存在");
		}

		if (SysTool.dirExists(Application.DIR_PORTRAIT.getDisk(), true)<1) {
			RequestUtils.deleteAnnexs(files);
			throw new AppException(SysErr.SYS_UPLOAD_ERR, "创建文件夹失败.");
		}

		// 删除已有照片
		SysTool.deleteFile(Application.DIR_PORTRAIT.getDisk()+"/"+exist.getPortrait());

		// 文件存到正式目录
		FileDoc doc = files.get(0);
		// 获取照片上传后的磁盘文件名(无路径)
		String fileName = SysTool.getFileName(doc.getFile());

		File origin = new File(doc.getFile());
		
		File dest = new File(Application.DIR_PORTRAIT.getDisk()+"/"+fileName);
		origin.renameTo(dest);

		return dao.updatePortrait(exist.getId(), fileName);
	}

	/**
	 * 修改用户状态
	 * 
	 * @param uid
	 * @param corpUid
	 * @param state
	 * @return
	 */
	public void setUserState(String uid, String corpUid, int state) {
		SysUserBean exists = dao.getUserByUid(uid);
		if (exists==null || (!StrTool.isBlank(corpUid) && !corpUid.equals(exists.getCorpUid()))) 
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND,"无此企业用户.");

		if (exists.getState()==state) return;

		dao.setUserState(exists.getId(), state);
		
		// 冻结或者用户非激活状态，需要让用户会话失效
		if (state!=SysUserBean.STATE_NORMAL) {
			SessionTool.invalidSession(exists.getUid());
		}
	}

	private boolean validRole(String role, List<SysRoleVO> list) {
		boolean ret = false;
		for(int i=0; i<list.size(); i++) {
			if (role.equals(list.get(i).getRoleName())){
				ret = true;
				break;
			}
		}
		return ret;
	}

	/**
	 * 授权企业用户帐号指定角色
	 * 
	 * @param corpUid 企业id
	 * @param id 帐号记录id
	 * @param roles 授予角色列表
	 * @return
	 */
	public void authRole(String corpUid, long id, List<String> roles) {
		SysUserBean user = dao.getUser(id);
		if (user==null || (!StrTool.isBlank(corpUid) && !corpUid.equals(user.getCorpUid())))
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "无此用户.");

		//获取企业所有角色，用于验证提交的角色有效性
		List<SysRoleVO> list = SysRoleUtils.queryRole(user.getCorpUid());
		if (list==null || list.size()==0) 
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "帐号所属企业,未定义任何角色.");

		//验证提交角色
		int i = 0;
		while (i<roles.size()) {
			if (!validRole(roles.get(i), list)) roles.remove(i);
			else i++;
		}
		if (roles.size()==0) 
			throw new AppException(SysErr.SYS_PARAM_ERR, "未设置有效的角色");

		dao.updateRole(id, roles);
		SessionTool.refreshCorpUserRoleFuncs(user.getCorpUid(), user.getUid(), roles);
	}

	/**
	 * 重置指定帐户密码
	 * 
	 * @param corpUid 企业id
	 * @param id 帐号记录id
	 * @param pwd 重置的密码
	 * @return
	 */
	public int setPwd(String corpUid, long id, String pwd) {
		SysUserBean user = dao.getUser(id);
		if (user==null || (!StrTool.isBlank(corpUid) && !corpUid.equals(user.getCorpUid())))
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "无此用户");
		
		return dao.updatePwd(id, pwd, null);
	}

	/**
	 * 更改帐户密码
	 * 
	 * @param uid 帐号uid
	 * @param oldPwd 原密码
	 * @param newPwd 新密码
	 * @return
	 */
	public int setMyPwd(String uid, String oldPwd, String newPwd) {
		// 判断用户是否存在
		SysUserBean user = dao.getUserByUid(uid);
		if (user==null) throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "无此用户");

		// 密码需要二次加密，保证和数据库里存储一致
		oldPwd = EncryptTool.md5Encode(StrTool.concat(oldPwd, user.getSalt()));
		if (!user.getPassword().equals(oldPwd)) 
			throw new AppException(SysErr.SYS_PARAM_ERR, "原密码错误");
		
		return dao.updatePwd(user.getId(), newPwd, null);
	}

	private String getMailTemplateText(String fileName) {
		InputStream is = StreamTool.loadFileToStream(SysUserUtils.class, fileName);
		if (is==null) return null;

		String ret = StreamTool.inputStreamToString(is, "utf-8");
		StreamTool.closeInputStream(is);
		return ret;
	}

	/**
	 * 发送找回密码邮件
	 * 
	 * @param account 帐户
	 * @return
	 */
	public int sendFindPwdMail(String account) {
		// 判断用户是否存在
		SysUserVO user = dao.getUserByAccount(account);
		if (user==null) 
			throw new AppException(SysErr.SYS_DATA_NOT_FOUND, "无此用户");

		if (user.getState()==SysUserBean.STATE_FROZEN) 
			throw new AppException(SysErr.USER_FROZEN, "帐户已冻结，无法重置密码");

		MailParam param = SysParamUtils.getMailParam();
		if (param==null) 
			throw new AppException(SysErr.SYS_PARAM_ERR, "未配置发送邮件相关参数");
		
		// 加载邮件模板
		String template = getMailTemplateText("/config/template/findPwd.tpl");
		if (StrTool.isBlank(template)) 
			throw new AppException(SysErr.SYS_PARAM_ERR, "未找到找回密码邮件模板");

		String code = CheckCodeTool.generateCheckCode(8);
		// 替换模板内容
		String webUrl = SysParamUtils.getParam("weburl");
		if (StrTool.isBlank(webUrl))  throw new AppException(SysErr.SYS_PARAM_ERR, "系统参数未配置网站地址");
		
		template = template.replaceAll("【nick】", StrTool.isBlank(user.getNick()) ? "会员" : user.getNick());
		template = template.replaceAll("【weburl】", SysParamUtils.getParam("weburl"));
		template = template.replaceAll("【uid】", user.getUid());
		template = template.replaceAll("【code】", code);

		MailMsg mail = new MailMsg();
		mail.setHost(param.getMailHost());
		mail.setPort(param.getMailPort());
		mail.setTls(param.isMailTLS());
		
		mail.setSubject("找回密码");
		mail.setSender(param.getMailAccount());
		mail.setPassword(param.getMailPassword());
		mail.setReceiver(account);
		mail.setMessage(template);

		if (!MailTool.sendMail(mail)) 
			throw new AppException(SysErr.CODE_FAILURE, "找回密码邮件发送失败");

		CodeExpiry findPwd = new CodeExpiry();
		findPwd.setCode(code);									// 验证码
		findPwd.setExpiry(DateTimeTool.incDay(new Date(), 1));	// 有效期为1天
		
		Map<String, Object> ext = user.getExtObj();
		ext.put("findPwd", findPwd);
		return dao.updateExt(user.getId(), JsonTool.objectToJson(ext));
	}

	/**
	 * 发送找回密码邮件
	 * 
	 * @param uname 帐户
	 * @return
	 */
	public int resetPwdByCode(String uid, String code, String newPwd) {
		// 判断用户是否存在
		SysUserVO user = dao.getUserByUid(uid);
		if (user==null)
			throw new AppException(SysErr.USER_USER_NOT_FOUND);

		Map<String, Object> ext = user.getExtObj();
		CodeExpiry findPwd = JsonTool.objectToBean(ext.get("findPwd"), CodeExpiry.class);
		if (findPwd==null || !code.equalsIgnoreCase(findPwd.getCode())) 
			throw new AppException(SysErr.SYS_CHECKCODE_ERR, "找回密码验证码无效");

		if (DateTimeTool.daysBetween(new Date(), findPwd.getExpiry())>1) 
			throw new AppException(SysErr.SYS_CHECKCODE_ERR, "验证码已过期");

		ext.remove("findPwd");
		return dao.updatePwd(user.getId(), newPwd, JsonTool.objectToJson(ext));
	}

	/**
	 * 授权企业用户帐号指定角色
	 * 
	 * @param corpUid 企业id
	 * @param q 帐号、密码或手机号
	 * @param page
	 * @param pagesize
	 * @return
	 */
	public SysUserVO getUser(String corpUid, String uid) {
		SysUserVO user = dao.getUserByUid(uid);
		if (user==null || (!StrTool.isBlank(corpUid) && !corpUid.equals(user.getCorpUid()))) 
			return null;

		return user;
	}
	
	/**
	 * 授权企业用户帐号指定角色
	 * 
	 * @param req
	 * @return
	 */
	public Page<SysUserVO> queryUser(QueryUserRequest req) {
		return dao.queryUser(req);
	}

	//////////////////////////////////////////////// 静态接口 /////////////////////////////////////////////

	/**
	 * 获取用户信息
	 * 
	 * @param id
	 * @return
	 */
	public static SysUserVO getUser(long id) {
		SysUserDao dao = new SysUserDao();
		return dao.getUser(id);
	}

	/**
	 * 获取用户信息
	 * 
	 * @param account
	 * @return
	 */
	public static SysUserVO getUserByUid(String uid) {
		SysUserDao dao = new SysUserDao();
		return dao.getUserByUid(uid);
	}
	
	/**
	 * 获取用户信息
	 * 
	 * @param account
	 * @return
	 */
	public static SysUserVO getUserByAccount(String account) {
		SysUserDao dao = new SysUserDao();
		return dao.getUserByAccount(account);
	}

	/**
	 * 更新最后登录时间
	 * 
	 * @param id
	 * @param ip
	 * @param time
	 */
	public static void updateLogin(long id, String ip, Date time) {
		SysUserDao dao = new SysUserDao();
		dao.updateLoginTime(id, ip, time);
	}

	/**
	 * 移除指定企业用户帐号指定角色
	 * 
	 * @param corpId
	 * @param roleName
	 * @return
	 */
	public static void removeRole(String corpId, String roleName) {
		SysUserDao dao = new SysUserDao();
		List<SysUserVO> users = dao.getCorpUser(corpId);
		if (users==null || users.size()==0)
			return;

		// 移除对应企业用户帐号拥有该角色的帐户角色
		for(int i=0; i<users.size(); i++) {
			List<String> roles = users.get(i).getRoleList();
			if (roles!=null && roles.contains(roleName)) {
				roles.remove(roleName);
				dao.updateRole(users.get(i).getId(), roles);
			}
		}		
	}

	/**
	 * 移除指定企业用户帐号指定角色
	 * 
	 * @param corpUid
	 * @param oldRole
	 * @param newRole
	 * @return
	 */
	public static void updateRole(String corpUid, String oldRole, String newRole) {
		SysUserDao dao = new SysUserDao();
		List<SysUserVO> users = dao.getCorpUser(corpUid);
		if (users==null || users.size()==0)
			return;

		// 更新对应企业用户帐号拥有该角色的帐户角色
		for(int i=0; i<users.size(); i++) {
			List<String> roles = users.get(i).getRoleList();
			if (roles.contains(oldRole)) {
				roles.remove(oldRole);
				roles.add(newRole);
				dao.updateRole(users.get(i).getId(), roles);
			}
		}
	}
}
